/*
 * dmcache.c
 *
 *  Created on: Apr 8, 2012
 *      Author: kody
 */
#include "cache.h"
#include "dmcache.h"

#define INDEXMASK 0xFFFFFFFF

static int numBlocks[3]={0,0,0};
static int numIndexBits[3]={0,0,0};
static int numTagBits[3]={0,0,0};
static int numBitsWordInBlock[3]={0,0,0};
static int numBitsByteInWord[3]={2,2,2};

boolean createDMCache(cache_s* cache)
{
	dmcacheblock* newcache;
	int blockSize;
	int temp;

	PRINT("\nCreating the DM Cache\n");

	/*Pull information for specific Cache level*/
	switch(cache->level)
	{
		case L1I:
		case L1D:
			numBlocks[cache->level]=L1_cache_size/L1_block_size;
			blockSize=L1_block_size;
			break;
		case L2:
			numBlocks[cache->level]=L2_cache_size/L2_block_size;
			blockSize=L2_block_size;
			break;
		default:
			printf("Invalid Cache Level\n");
			return FALSE;
	}

	/*Get number of index bits*/
	temp = numBlocks[cache->level];
	numIndexBits[cache->level] = 0;
	while(temp >>= 1)
	{
		numIndexBits[cache->level]++;
	}

	/*Get the number of bits deciding what word in the block*/
	temp = blockSize;
	numBitsWordInBlock[cache->level] = 0;
	while(temp >>= 1)
	{
		numBitsWordInBlock[cache->level]++;
	}

	/*Get the number of Tag bits*/
	numTagBits[cache->level] = WORD_SIZE-numIndexBits[cache->level]-numBitsWordInBlock[cache->level];

	/*Set the number of Bits per word in block*/
	numBitsWordInBlock[cache->level] -= 2;

	PRINT("NumBlocks:%d \n NumIndexBits:%d\n NumTagbits:%d\n numBitsWordInBlock:%d\n",
			numBlocks[cache->level],
			numIndexBits[cache->level],
			numTagBits[cache->level],
			numBitsWordInBlock[cache->level]);
	/*Allocate space for the cache*/
	newcache = (dmcacheblock*) malloc(numBlocks[cache->level]*sizeof(dmcacheblock));
	cache->cacheptr = (cachePointer)newcache;

	return TRUE;
}
boolean checkDMCache(cache_s* cache, uint32 reference, boolean rw)
{
	boolean retval;
	uint32 index=reference;
	uint32 tag=reference;
	uint32 mask;
	dmcacheblock* DMCache=(dmcacheblock*)cache->cacheptr;

	/*
	 * create mask
	 */
	// Init Mask
	mask = INDEXMASK;
	//Shift to right to get number of bits
	mask >>= (WORD_SIZE-numIndexBits[cache->level]);
	//Shift back into proper location compared to reference
	mask <<= (numBitsWordInBlock[cache->level]+numBitsByteInWord[cache->level]);

	/*
	 * Extract index
	 */
	index = (index & mask);
	index >>= (numBitsWordInBlock[cache->level]+numBitsByteInWord[cache->level]);
	/*
	 * Extract Tag
	 */
	tag = (tag >> (WORD_SIZE-numTagBits[cache->level]));
	/* Index into Cache and compare tag*/
	if((DMCache[index].tag==tag) && (DMCache[index].valid))
	{
		if ( (rw == TO_Store) && ((cache->level == L1D) || (cache->level == L2)))
		{
			DMCache[index].dirty=TRUE;
		}
		retval = TRUE;

		PRINT("Cachelevel: %d ref:%x   index:%x  tag:%x HIT\n",
				cache->level,reference, index, tag);
	}
	else
	{
		retval = FALSE;

		PRINT("Cachelevel: %d ref:%x   index:%x   tag:%x oldtag:%x MISS\n",
				cache->level,reference, index, tag, DMCache[index].tag);

	}

	return retval;
}
/*
 * addValueToDMCache
 * This function will add to the specified direct mapped cache the entry defined by reference
 *
 * Inputs:
 * 		cache_s* cache--Pointer to the Cache struct
 * 		uint32 reference-- THe reference value
 * outputs:
 * 		returns NOKICK (1) if No kickout
 * 		returns CLEAN (0) if Kickout
 * 		returns DIRTY (-1) if dirty kickout
 *
 */
kickout addValueToDMCache(cache_s* cache, uint32 reference, opcode isWrite)
{
	uint32 index=reference;
	uint32 tag=reference;
	uint32 mask;
	uint32 indexshift = (numBitsWordInBlock[cache->level]+numBitsByteInWord[cache->level]);
	uint32 tagshift = (WORD_SIZE-numTagBits[cache->level]);
	kickout ret={0,0};
	dmcacheblock* DMCache=(dmcacheblock*)cache->cacheptr;


	/*
	 * create mask
	 */
	// Init Mask
	mask = INDEXMASK;
	//Shift to right to get number of bits
	mask >>= (WORD_SIZE-numIndexBits[cache->level]);
	//Shift back into proper location compared to reference
	mask <<= (numBitsWordInBlock[cache->level]+numBitsByteInWord[cache->level]);

	/*
	 * Extract index
	 */
	index = (index & mask);
	index >>= indexshift;
	/*
	 * Extract Tag
	 */
	tag = (tag >> tagshift);

	if(!DMCache[index].valid)
	{
		DMCache[index].tag = tag;
		DMCache[index].valid = TRUE;
		DMCache[index].dirty = FALSE;
		ret.type = NOKICK;
		ret.evictedAddress = 0;
	}
	else if(DMCache[index].valid && !DMCache[index].dirty)
	{
		/*Kickout non dirty entry*/
		DMCache[index].tag=tag;
		DMCache[index].valid=TRUE;
		DMCache[index].dirty = FALSE;
		ret.type = CLEAN;
		ret.evictedAddress = 0;
	}
	else if(DMCache[index].valid && DMCache[index].dirty)
	{
		/*Kickout dirty entry*/
		uint32 evictedAddress = ((index << indexshift) | (DMCache[index].tag << tagshift));
		DMCache[index].tag=tag;
		DMCache[index].valid=TRUE;
		DMCache[index].dirty = FALSE;
		ret.type = DIRTY;
		ret.evictedAddress = evictedAddress;
	}

	if ( (isWrite == TO_Store) && ((cache->level == L1D) || (cache->level == L2)))
	{
		DMCache[index].dirty=TRUE;
	}

	return ret;
}

boolean deleteDMCache(cache_s* cache)
{
	dmcacheblock* dmcache=(dmcacheblock*)cache->cacheptr;
	PRINT("Freeing the space for the Cache\n");
	free(dmcache);
	return TRUE;
}
void printDMCache(cache_s* cache)
{
	uint32 index;
	dmcacheblock* DMCache=cache->cacheptr;
	for(index=0; index<numBlocks[cache->level];index++)
	{
		if(DMCache[index].valid)
		{
			CPRINT(stderr,"Index:%5x | V:%d D:%d Tag:%8x | \n",
					index,DMCache[index].valid,DMCache[index].dirty,DMCache[index].tag);
		}
	}
}
